/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.dess.mybatis.plugins.parser;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.znlh.demo.mybatisdemo.execute.SpringApplicationContext;
import com.znlh.demo.mybatisdemo.invoke.LocalInvoke;

import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @project-name:mybatis-demo
 * @package-name:com.znlh.demo.mybatisdemo.parser
 * @author:LiBo/Alex
 * @create-date:2020-06-16 13:18
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 */
public class LocalInvokeParser {

    private static final String DEFAULT_PRE_FIX_TOKEN = "#[";

    private static final String DEFAULT_POST_FIX_TOKEN = "]";

    private static final String DEFAULT_METHOD_NAME_TOKEN_L = "(";

    private static final String DEFAULT_METHOD_NAME_TOKEN_R = ")";

    private static final String DEFAULT_METHOD_PARAM_TOKEN_SPLIT = ",";

    private static final String DEFAULT_METHOD_PARAM_VALUE = "root";

    private static  String DEFAULT_ANALAYSIS_PATTERN = DEFAULT_PRE_FIX_TOKEN+"{0}.{1}({2})"+DEFAULT_POST_FIX_TOKEN;

    /**
     * 解析器 上下文对象
     * @param messageStr
     * @return
     */
    public static LocalInvoke parse(String messageStr){
        if(StringUtils.isNotBlank(messageStr) ){
            if(messageStr.startsWith(DEFAULT_PRE_FIX_TOKEN) && messageStr.endsWith(DEFAULT_POST_FIX_TOKEN)) {
               return parseCallerMethod(messageStr);
            }
        }
        throw new IllegalArgumentException("解析参数失败！");
    }

    /**
     * 解析调用方法操作处理
     * @param messageStr
     * @return
     */
    private static LocalInvoke parseCallerMethod(String messageStr){
        LocalInvoke localInvoke = new LocalInvoke();
            int index = messageStr.indexOf(DEFAULT_METHOD_NAME_TOKEN_L);
            if(index>0) {
                String classAndMethodStr = messageStr.substring(DEFAULT_PRE_FIX_TOKEN.length(),index);
                if (StringUtils.isNotBlank(classAndMethodStr)) {
                    int lastIndex = classAndMethodStr.lastIndexOf(".");
                    localInvoke.setMethodName(classAndMethodStr.substring(lastIndex+1));
                    localInvoke.setClassName(classAndMethodStr.substring(0,lastIndex));
                }
                int index2 = messageStr.indexOf(DEFAULT_METHOD_NAME_TOKEN_R);
                if(index2 > 0){
                    messageStr = messageStr.substring(index+1,index2);
                    String [] paramArray = StringUtils.split(messageStr,DEFAULT_METHOD_PARAM_TOKEN_SPLIT);
                    if(ArrayUtils.isNotEmpty(paramArray)){
                        localInvoke.setParamObject(Stream.of(paramArray).collect(Collectors.toList()));
                    }
                    return localInvoke;
                }
            }
        return localInvoke;
    }

    /**
     * 执行本地反射操作处理功能实现
     * @param localInvoke
     * @return
     */
    public static Object invoke(LocalInvoke localInvoke,Object contextObject,boolean remote) throws ClassNotFoundException {
        // 执行调研本地方法操作
        if(Objects.nonNull(localInvoke) && Objects.nonNull(contextObject)){
            Class invokeCLass = Class.forName(localInvoke.getClassName());
            Object invokeObject = null;
            if(!remote) {
                invokeObject = ReflectUtil.newInstance(invokeCLass);
            }
            Object[] realParam = new Object[localInvoke.getParamObject().size()];
            int i = 0;
            for(String param:localInvoke.getParamObject()){
                if(param.startsWith("root.")){
                    realParam[i++] =  ReflectUtil.getFieldValue(contextObject,param.replace("root.",""));
                }
            }
            if(remote) {
                invokeObject = SpringApplicationContext.getApplicationContext().getBean(Class.forName(localInvoke.getClassName()));
            }
            return ReflectUtil.invoke(invokeObject, ReflectUtil.getMethodByName(invokeCLass, localInvoke.getMethodName()), realParam);
        }
        return null;
    }

}
